//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<os.inl>
///	@path	~/src/config/
///	@date	2007/06/14
///	@desc	Platform related inlines.

#pragma once

#include "xeres.h"

#include "config/os.h"

#include "lib/utilities/debug.h"

namespace xeres {

	// __win32_error
	static inline void __win32_error( const wchar_t * printf_format , DWORD err )
	{
		wchar_t * lpMsgBuf = NULL;
		size_t len = ::FormatMessageW(
    			FORMAT_MESSAGE_ALLOCATE_BUFFER |
				FORMAT_MESSAGE_FROM_SYSTEM |
				FORMAT_MESSAGE_IGNORE_INSERTS ,
				NULL,
				err ,
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) ,
				(wchar_t *)&lpMsgBuf ,
				0 ,
				NULL );
		lpMsgBuf[ len - 3 ] = 0;
		TRACE_ERROR( printf_format , lpMsgBuf );
		::LocalFree( lpMsgBuf );
	}

	template< typename _iter > static inline void normalize_slash( _iter filename )
	{
		for( ; *filename != 0 ; filename++ )
			if( ( *filename == '/' ) || ( *filename == '\\') )
				*filename = DIR_SEPARATOR;
	}

	template< typename _iter > static inline void normalize_slash( _iter begin , _iter end )
	{
		_iter it = begin;
		_iter tail = it;
		for( ; it != end ; ++it , ++tail )
		{
			if( ( ( *it == '/' ) || ( *it == '\\') ) && it != begin && it != begin + 1 )
			{
				*tail = DIR_SEPARATOR;
				while( ++it != end )
				{
					if( ( *it != '/' && *it != '\\' ) )
						break;
				} 
				--it;
			}
			else
			{
				*tail = *it;
			}
		}
		if( tail != end )
			*tail = 0;
	}

	template< typename _iter > static inline void normalize_slash( _iter begin , _iter end , char dir )
	{
		_iter it = begin;
		_iter tail = it;
		for( ; it != end ; ++it , ++tail )
		{
			if( ( ( *it == '/' ) || ( *it == '\\') ) && it != begin && it != begin + 1 )
			{
				*tail = dir;
				while( ++it != end )
				{
					if( ( *it != '/' && *it != '\\' ) )
						break;
				} 
				--it;
			}
			else
			{
				*tail = *it;
			}
		}
		if( tail != end )
			*tail = 0;
	}

	static inline void spin_lock_init( int * spin_lock )
	{
		*spin_lock = LOCK_FREE;
	}

	static inline void spin_spin( int volatile * spin_lock , int spin_count )
	{
		int b = spin_count;
		/* yield on WIN32 */
		::SwitchToThread();

		/* spin loop */
	SPIN:
		b -= 1;
		if( b == 0 )
			return;

		/* pause hint, maybe useful on P4 or later architecture */
		_mm_pause();

		/* is this works ? */
		if( *spin_lock == LOCK_FREE )
			return;

		goto SPIN;
	}

	static inline bool spin_spin_val( int volatile * spin_lock , const int comparion , int spin_count )
	{
		int b = spin_count;
		/* yield on WIN32 */
		::SwitchToThread();

		/* spin loop */
	SPIN:
		b -= 1;
		if( b == 0 )
			return false;

		/* pause hint, maybe useful on P4 or later architecture */
		_mm_pause();

		/* is this works ? */
		if( *spin_lock == comparion )
			return true;

		goto SPIN;
	}

	static inline void spin_lock_lock( int volatile * spin_lock , int spin_count )
	{
		int b = spin_count;

	/* currently VC8 does not support inline assemble in X64 system */
#if (!defined _WIN64) || (defined __ICL)

		__asm
		{
	GET_LOCK:
			mov	esi , spin_lock;
			mov eax , LOCK_TAKEN;
			xchg eax , [esi] ; /* Try to get lock  */
			cmp eax , LOCK_FREE ; /* Test if successful */
			jne SPIN_LOOP;
		}
		return;
	SPIN_LOOP:
		__asm
		{
			sub b , 1;
			jz SWITCH;

			pause; /* pause hint, maybe useful on P4 or later architecture */
			cmp [esi] , LOCK_FREE; /* check lock availability  */
			jne SPIN_LOOP;
			jmp GET_LOCK;
		}
		return;
	SWITCH:
		b = 50;
		::SwitchToThread();
		goto GET_LOCK;

#else

	GET_LOCK:
		/* intrinsic on VC8 */
		if( ::_InterlockedExchange( (long*)spin_lock , LOCK_TAKEN ) == LOCK_FREE )
			return;

		/* yield */
		::SwitchToThread();
	SPIN:
		b -= 1;
		if( b == 0 )
		{
			b = 50;
			goto GET_LOCK;
		}

		/* pause hint, maybe useful on P4 or later architecture */
		_mm_pause();

		/* is this works ? */
		if( *spin_lock == LOCK_FREE )
			goto GET_LOCK;

		goto SPIN;

#endif /* (!defined _WIN64) && (!defined __ICL) */

	}

	static inline LockState spin_lock_trylock( int volatile * spin_lock )
	{
		return (LockState)::_InterlockedExchange( (long*)spin_lock , LOCK_TAKEN );
	}

	static inline void spin_lock_unlock( int volatile * spin_lock )
	{
		*spin_lock = LOCK_FREE;
	}

	static inline void atom_inc( int volatile * var )
	{
		::_InterlockedIncrement( (long volatile*)var );
	}

	static inline void atom_dec( int volatile * var )
	{
		::_InterlockedDecrement( (long volatile*)var );
	}

	static inline void atom_add( int volatile * var , int value )
	{
		::_InterlockedExchangeAdd( (long volatile*)var , (long)value );
	}

	static inline int atom_xchg_add( int volatile * var , int value )
	{
		return (int)::_InterlockedExchangeAdd( (long volatile*)var , (long)value );
	}

	static inline int atom_xchg( int volatile * var , int value )
	{
		return (int)::_InterlockedExchange( (long volatile*)var , (long)value );
	}

	static inline void * __fastcall atom_xchg_ptr( void * volatile * dst , void * src )
	{
#if (!defined _WIN64)
		__asm
		{
			mov		esi , dst
			mov		eax , src;
			xchg	eax , [esi];
		}
#elif (defined __ICL)
		__asm
		{
			mov		rbx , dst
			mov		rax , src;
			xchg	rax , [esi];
		}
#else
		return ::_InterlockedExchangePointer( dst , src );
#endif
	}

	static inline void * atom_cmpxchg_ptr( void * volatile * dst , void * src , void * comparend )
	{
#if (!defined _WIN64)
		return (void*)::_InterlockedCompareExchange( (LONG*)dst , (LONG)src , (LONG)comparend );
#else
		return ::_InterlockedCompareExchangePointer( dst , src , comparend );
#endif/*_WIN64*/
	}

} // namespace xeres
